package me.kennylee.mobile.sms.code;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import me.kennylee.cache.CacheHelper;
import me.kennylee.mobile.sms.code.validator.SendCodeValidator;
import me.kennylee.mobile.sms.code.validator.SendIntervalValidator;
import me.kennylee.mobile.sms.code.validator.SmsCountValidator;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * <p> 验证码发送的工具类  </p>
 * <p>Created on 2/2/2018.</p>
 *
 * @author kennylee
 */
public abstract class VerifyCodeSender {

    String apiKey;
    String apiSecret;

    private List<SendCodeValidator> validators = Collections.unmodifiableList(Lists.newArrayList());
    private List<SenderObserver> observers = Collections.unmodifiableList(Lists.newArrayList());

    VerifyCodeSender(String apiKey, String apiSecret) {
        this.apiKey = apiKey;
        this.apiSecret = apiSecret;
        registeredValidators();
        registeredObservers();
    }

    protected abstract SmsSender getSmsSender();

    protected abstract SmsMessage getSmsMessage(String mobile, Map<String, String> params);

    /**
     * <p>发送短信验证码，验证码默认生成方式。</p>
     *
     * @param mobile 发送短信验证码的目标手机号。
     * @return SmsResponse
     * @see #generateCode()
     */
    public SmsResponse send(String mobile) {
        return this.send(mobile, null);
    }

    /**
     * <p>发送指定短信验证码。</p>
     *
     * @param mobile 发送短信验证码的目标手机号。
     * @param code   指定的验证码
     * @return SmsResponse
     */
    public SmsResponse send(final String mobile, final String code) {
        return this.send(mobile, code, Maps.newHashMap());
    }

    /**
     * <p>发送指定短信消息。</p>
     *
     * @param mobile 发送短信验证码的目标手机号。
     * @param code   指定的验证码
     * @param params 扩展信息
     * @return SmsResponse
     */
    public SmsResponse send(final String mobile, final String code, final Map<String, String> params) {
        SmsResponse checkResponse = check(mobile);
        if (!checkResponse.isSuccess()) {
            return checkResponse;
        }
        String sendCode;
        if (StringUtils.isBlank(code)) {
            sendCode = generateCode();
        } else {
            sendCode = code;
        }
        SmsSender smsSender = getSmsSender();
        Map<String, String> m = params == null || params.isEmpty() ? Maps.newHashMap() : params;
        if (m.containsKey("code")) {
            throw new RuntimeException("params cant contains key: code");
        }
        m.put("code", sendCode);//短信模板中，验证码默认名字为code
        SmsResponse response = smsSender.send(getSmsMessage(mobile, m));
        if (!getObservers().isEmpty()) {
            getObservers().forEach(observer -> observer.update(new String[]{mobile, sendCode}, response));
        }
        return response;
    }

    public void remove(final String mobile) {
        if (!getObservers().isEmpty()) {
            getObservers().forEach(observer -> observer.clear(new String[]{mobile}));
        }
    }

    SmsMessage createSmsMessage(String mobile, Map<String, String> params, String templateCode, String signName) {
        SmsMessage sms = SmsMessage.of();
        sms.setRec_num(mobile);
        sms.setTemplate(templateCode);
        sms.addParams(params);
        sms.setSign_name(signName);
        return sms;
    }


    protected SmsResponse check(String mobile) {
        if (validators.isEmpty()) {
            return SmsResponse.success();
        } else {
            SmsResponse response = null;
            for (SendCodeValidator validator : validators) {
                response = validator.check(mobile);
                if (!response.isSuccess()) {
                    break;
                }
            }
            return response;
        }
    }

    public void addObserver(SenderObserver observer) {
        List<SenderObserver> l = Lists.newArrayList(observers);
        l.add(observer);
        observers = Collections.unmodifiableList(l);
    }

    public SenderObserver removeObserver(int idx) {
        List<SenderObserver> l = Lists.newArrayList(observers);
        SenderObserver e = l.remove(idx);
        observers = Collections.unmodifiableList(l);
        return e;
    }

    public List<SenderObserver> getObservers() {
        return observers;
    }

    protected void registeredObservers() {
        // add mobile verify code
        if (StringUtils.isNotBlank(getCodeLiveCacheName())) {
            addObserver(new SenderObserver() {
                @Override
                public void update(String[] params, SmsResponse smsResponse) {
                    if (smsResponse.isSuccess()) {
                        CacheHelper.put(getCodeLiveCacheName(), params[0], params[1]);
                    }
                }

                @Override
                public void clear(String[] params) {
                    CacheHelper.remove(getCodeLiveCacheName(), params[0]);
                }
            });
        }
        // add add interval count
        if (StringUtils.isNotBlank(getSmsIntervalCacheName())) {
            addObserver(new SenderObserver() {
                @Override
                public void update(String[] params, SmsResponse smsResponse) {
                    if (smsResponse.isSuccess()) {
                        //如果间隔的缓存还在的话，代表还没超过间隔，超过间隔缓存就清除了。
                        CacheHelper.addCount(getSmsIntervalCacheName(), params[0]);
                    }
                }

                @Override
                public void clear(String[] params) {
                    CacheHelper.remove(getSmsIntervalCacheName(), params[0]);
                }
            });
        }
        // add mobile total count
        if (StringUtils.isNotBlank(getMobileSmsCountCacheName())) {
            addObserver(new SenderObserver() {
                @Override
                public void update(String[] params, SmsResponse smsResponse) {
                    if (smsResponse.isSuccess()) {
                        CacheHelper.addCount(getMobileSmsCountCacheName(), params[0]);
                    }
                }

                @Override
                public void clear(String[] params) {
                    CacheHelper.remove(getMobileSmsCountCacheName(), params[0]);
                }
            });
        }
    }

    protected abstract String getMobileSmsCountCacheName();

    protected abstract String getSmsIntervalCacheName();

    protected abstract String getCodeLiveCacheName();

    private void registeredValidators() {
        addValidator(new SendIntervalValidator());
        addValidator(new SmsCountValidator());
    }

    public static String generateCode() {
        return RandomStringUtils.randomNumeric(6);
    }

    public void addValidator(SendCodeValidator validator) {
        List<SendCodeValidator> l = Lists.newArrayList(validators);
        l.add(validator);
        validators = Collections.unmodifiableList(l);
    }

    public SendCodeValidator removeValidator(int idx) {
        List<SendCodeValidator> l = Lists.newArrayList(validators);
        SendCodeValidator e = l.remove(idx);
        validators = Collections.unmodifiableList(l);
        return e;
    }

    public List<SendCodeValidator> getValidators() {
        return validators;
    }
}
